//SA omp
//$Id: sa_omp.h 441 2010-04-09 17:27:20Z Oleg.Bulychov $
#pragma once

#include "src/heo/include/easy_omp.h"
#include "src/heo/include/ring_coop.h"
#include "src/heo/include/xml_file.h"
#include "sa_common.h"

//__________________________________________________________________________________
/**
The Multistart Model with Ring Cooperation
*/
template<class T>
class SA_OMP:
    public SA<T>
{
public:
    typedef typename SA<T>::TEnvironment TEnvironment;
    typedef typename SA<T>::TSolution TSolution;

    typedef typename SA<T>::TDirection TDirection;
    typedef typename SA<T>::TCostType  TCostType;

protected:
    //TODO move to template arg?
    ring_cooperation<omp::recv, omp::isend, omp::isend> coop_;

public:
    template<class TInOut>
    void solve(TInOut& io)
    {
        int np = omp_get_max_threads();
        omp::solver_fork< TInOut, SA_OMP<T> > f(io, *this);
        omp::communicator::run(np, f);
    }

    template<class V>
    void accept(V& v)
    {
        v(static_cast<SA<T>&>(*this), "/");
        v(VAR_(coop));
    }

    template<class TInOutSolution>
    void solve_OMP(Solution_Pool<TInOutSolution>& pool, omp::communicator& comm)
    {
        ASSERT_(this->rank_ == comm.rank());
        int np   = comm.size();
        int prev = (np + this->rank_ - 1) % np;
        int next = (this->rank_ + 1)      % np;

        this->init_From_Pool(pool);
        this->set_Initial_Temperature();
        this->calc_Max_Step();
        this->show_Progress(0);

        for (int step = 1; step <= this->max_step_; ++step)
        {
            if (this->coop_.cooperate(*this, this->rank_, prev, next, np, step,
                comm))
            {
                if (this->coop_.is_Async_Mode())
                {
                    if (this->sent_stop_)
                        break;
                }
                else
                {
                    volatile static long g_stop = 0;
                    if (this->pending_stop_)
                        g_stop = 1;
                    comm.barrier();
                    if (g_stop)
                        break;
                }
            }
            else
            {
                this->move();
            }

            if (this->is_Acceptable_Solution())
            {
                this->accept_Solution();
                this->show_Progress(step);
                if (this->update_policy_ == sa_update_if_better)
                {
                    this->update_Best_Solution();
                }
            }

            if (this->stop(step))
            {
                if (1 == np || !this->coop_.get_Rate())
                    break;//:no coop - no need for 'pending stop'
                this->pending_stop_ = 1;
            }

            this->update_Temperature(step);
        }

        this->clear_OMP(comm);

        TSolution const& final_sol = this->get_Final_Solution();
        //:final_sol --> pool

        if (0 == this->rank_)
        {
            pool.sol_list_.resize(1);
            pool.sol_list_.front().init(this->env_, this->rnd_);
            pool.sol_list_.front().assign(final_sol);
            if (this->env_->debug_level_)
                std::cout << "0: cost = " << final_sol.get_Cost() << lf;
        }
        comm.barrier();
#pragma omp critical
        {
            if (0 != this->rank_)
            {
                if (TDirection()(final_sol.get_Cost(), pool.sol_list_.front().get_Cost()))
                {
                    pool.sol_list_.front().assign(final_sol);
                    if (this->env_->debug_level_)
                        std::cout << this->rank_ << ":*cost = " << final_sol.get_Cost() << lf;
                }
                else
                {
                    if (this->env_->debug_level_)
                        std::cout << this->rank_ << ": cost = " << final_sol.get_Cost() << lf;
                }
            }
        }
    }

private:
    void clear_OMP(omp::communicator& comm)
    {
        comm.barrier();
        if (!coop_.is_Async_Mode())
            return;
        comm.clear_Data();
        comm.barrier();
    }
};
//__________________________________________________________________________________
//__________________________________________________________________________________

//EOF!
